﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
using System.Security.Cryptography;

namespace SecureGRF
{
    [StructLayout(LayoutKind.Sequential)]
    public struct ImageSectionHeader
    {
        [MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
        public byte[] Name;

        public uint VirtualSize;
        public uint VirtualAddress;
        public uint SizeOfRawData;
        public uint PointerToRawData;
        public uint PointerToRelocations;
        public uint PointerToLinenumbers;
        public ushort NumberOfRelocations;
        public ushort NumberOfLinenumbers;
        public uint Characteristics;
    }

    public class ConfigureProfile
    {
        private readonly int Dos_e_lfanew = 0x3c;
        private readonly int Nt_NumberOfSections = 6;
        private readonly int NtHeaderSize = 0xf8;

        public ConfigureProfile() { }

        public string DoConfigure(string clientFile, string kernelFile, string password, string outputPath)
        {
            try
            {
                byte[] clientHash = CrackClient(clientFile, kernelFile, outputPath);
                CrackKernel(clientHash, password, kernelFile, outputPath);
                return "1Configure done!";
            }
            catch (Exception ex)
            {
                return "0" + ex.Message;
            }
        }

        private byte[] CrackClient(string clientFile, string kernelFile, string outputPath)
        {
            FileStream fSource = new FileStream(clientFile, FileMode.Open, FileAccess.Read, FileShare.Read);
            byte[] buf;
            try
            {
                buf = new byte[(int)fSource.Length];
                fSource.Read(buf, 0, buf.Length);
            }
            finally
            {
                fSource.Close();
            }
            int index = FindBuf(buf, Encoding.ASCII.GetBytes("cps.dll\0"));
            if (index < 0) throw new Exception("The client executable file is not the orignal file!");
            byte[] bufEmpty = new byte[7];
            Array.Copy(bufEmpty, 0, buf, index, bufEmpty.Length);
            byte[] bufKernel = Encoding.Default.GetBytes(kernelFile);
            Array.Copy(bufKernel, 0, buf, index, bufKernel.Length);

            BinaryReader reader = new BinaryReader(new MemoryStream(buf));
            reader.BaseStream.Position = Dos_e_lfanew;
            int posNtHeader = reader.ReadInt32();
            reader.BaseStream.Position = posNtHeader + Nt_NumberOfSections;
            int sectionNumber = reader.ReadInt16();
            reader.BaseStream.Position = posNtHeader + NtHeaderSize;
            int hashPos = 0, hashSize = 0;
            bool isFind = false;
            while (true)
            {
                byte[] name = reader.ReadBytes(8);
                reader.ReadInt32();
                reader.ReadInt32();
                hashSize = reader.ReadInt32();
                hashPos = reader.ReadInt32();
                reader.ReadInt32();
                reader.ReadInt32();
                reader.ReadInt32();
                int characteristics = reader.ReadInt32();
                if (characteristics == 0) break;
                if (Encoding.ASCII.GetString(name).ToLower().StartsWith(".rsrc\0"))
                {
                    isFind = true;
                    break;
                }
            }
            reader.Close();
            if (!isFind) throw new Exception("The client executable file is not the orignal file!");
            SHA256 sha256 = SHA256Managed.Create();
            byte[] hash = sha256.ComputeHash(buf, hashPos, hashSize);

            FileStream fTarget = new FileStream(Path.Combine(outputPath, Path.GetFileName(clientFile)), FileMode.Create, FileAccess.Write, FileShare.None);
            try
            {
                fTarget.Write(buf, 0, buf.Length);
            }
            finally
            {
                fTarget.Close();
            }
            return hash;
        }

        private void CrackKernel(byte[] clientHash, string password, string kernelFile, string outputPath)
        {
            FileStream fSource = new FileStream("SecureGRFLib.dll", FileMode.Open, FileAccess.Read, FileShare.Read);
            byte[] buf;
            try
            {
                buf = new byte[(int)fSource.Length];
                fSource.Read(buf, 0, buf.Length);
            }
            finally
            {
                fSource.Close();
            }

            BinaryReader reader = new BinaryReader(new MemoryStream(buf));
            reader.BaseStream.Position = Dos_e_lfanew;
            int posNtHeader = reader.ReadInt32();
            reader.BaseStream.Position = posNtHeader + Nt_NumberOfSections;
            int sectionNumber = reader.ReadInt16();
            reader.BaseStream.Position = posNtHeader + NtHeaderSize;
            int pos = 0, size = 0;
            bool isFind = false;
            while (true)
            {
                byte[] name = reader.ReadBytes(8);
                reader.ReadInt32();
                reader.ReadInt32();
                size = reader.ReadInt32();
                pos = reader.ReadInt32();
                reader.ReadInt32();
                reader.ReadInt32();
                reader.ReadInt32();
                int characteristics = reader.ReadInt32();
                if (characteristics == 0) break;
                if (Encoding.ASCII.GetString(name).ToLower().StartsWith(".rsrc\0"))
                {
                    isFind = true;
                    break;
                }
            }
            reader.Close();
            if (!isFind) throw new Exception("The kernel file is not the orignal file!");

            int index = FindBuf2(buf, pos, size, Encoding.ASCII.GetBytes("CONFIG FILL\0"));
            if (index < 0) throw new Exception("The kernel file is not the orignal file!");

            byte[] ivCode = new byte[16], keyCode = new byte[32];
            Random ivRand = new Random((int)0x4d589e05), keyRand = new Random((int)0x125fcb56);
            for (int i = 0; i < ivCode.Length; i++) ivCode[i] = (byte)ivRand.Next();
            for (int i = 0; i < keyCode.Length; i++) keyCode[i] = (byte)keyRand.Next();
            Rijndael aes = RijndaelManaged.Create();
            MemoryStream ms = new MemoryStream();
            CryptoStream cs = new CryptoStream(ms, aes.CreateEncryptor(keyCode, ivCode), CryptoStreamMode.Write);
            BinaryWriter writer = new BinaryWriter(cs);
            writer.Write(clientHash);
            SHA256 sha256 = SHA256Managed.Create();
            writer.Write(sha256.ComputeHash(Encoding.Unicode.GetBytes(password)));
            cs.FlushFinalBlock();
            ms.Position = 0;
            byte[] bufData = new byte[(int)ms.Length];
            ms.Read(bufData, 0, bufData.Length);
            ms.Close();

            Random rand = new Random();
            for (int i = index; i < index + 1024; i++) buf[i] = (byte)rand.Next();
            buf[index + 0x253] = (byte)bufData.Length;
            Array.Copy(bufData, 0, buf, index + 0x254, bufData.Length);

            FileStream fTarget = new FileStream(Path.Combine(outputPath, kernelFile), FileMode.Create, FileAccess.Write, FileShare.None);
            try
            {
                fTarget.Write(buf, 0, buf.Length);
            }
            finally
            {
                fTarget.Close();
            }
        }

        private int FindBuf(byte[] buf, byte[] sub)
        {
            for (int i = buf.Length - sub.Length - 1; i >= 0; i--)
            {
                bool isEqual = true;
                for (int j = 0; j < sub.Length; j++)
                {
                    if (buf[i + j] != sub[j])
                    {
                        isEqual = false;
                        break;
                    }
                }
                if (isEqual) return i;
            }
            return -1;
        }

        private int FindBuf2(byte[] buf, int index, int length, byte[] sub)
        {
            for (int i = index; i < index + length - sub.Length; i++)
            {
                bool isEqual = true;
                for (int j = 0; j < sub.Length; j++)
                {
                    if (buf[i + j] != sub[j])
                    {
                        isEqual = false;
                        break;
                    }
                }
                if (isEqual) return i;
            }
            return -1;
        }
    }
}
